/*
Copyright 2017 Google Inc.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

package sqlparser

import (
	"strings"

	"gitee.com/xuender/go-mysqlstack/sqlparser/depends/sqltypes"
)

// SQLNode defines the interface for all nodes
// generated by the parser.
type SQLNode interface {
	Format(buf *TrackedBuffer)
}

// Statements.
type (
	// Statement represents a statement.
	Statement interface {
		iStatement()
		SQLNode
	}

	// SelectStatement any SELECT statement.
	SelectStatement interface {
		iSelectStatement()
		iStatement()
		iInsertRows()
		AddOrder(*Order)
		SetLimit(*Limit)
		SQLNode
	}

	// Select represents a SELECT statement.
	Select struct {
		Cache       string
		Comments    Comments
		Distinct    string
		Hints       string
		SelectExprs SelectExprs
		From        TableExprs
		Where       *Where
		GroupBy     GroupBy
		Having      *Where
		OrderBy     OrderBy
		Limit       *Limit
		Lock        string
		ForBackup   string
	}

	// Union represents a UNION statement.
	Union struct {
		Type        string
		Left, Right SelectStatement
		OrderBy     OrderBy
		Limit       *Limit
		Lock        string
	}

	// ParenSelect is a parenthesized SELECT statement.
	ParenSelect struct {
		Select SelectStatement
	}

	// Insert represents an INSERT or REPLACE statement.
	// Per the MySQL docs, http://dev.mysql.com/doc/refman/5.7/en/replace.html
	// Replace is the counterpart to `INSERT IGNORE`, and works exactly like a
	// normal INSERT except if the row exists. In that case it first deletes
	// the row and re-inserts with new values. For that reason we keep it as an Insert struct.
	// Replaces are currently disallowed in sharded schemas because
	// of the implications the deletion part may have on vindexes.
	Insert struct {
		Action   string
		Comments Comments
		Ignore   string
		Table    TableName
		Columns  Columns
		Rows     InsertRows
		OnDup    OnDup
	}

	// Update represents an UPDATE statement.
	Update struct {
		Comments Comments
		Table    TableName
		Exprs    UpdateExprs
		Where    *Where
		OrderBy  OrderBy
		Limit    *Limit
	}

	// Delete represents a DELETE statement.
	Delete struct {
		Comments Comments
		Table    TableName
		Where    *Where
		OrderBy  OrderBy
		Limit    *Limit
	}

	// Set represents a SET statement.
	Set struct {
		Comments Comments
		Exprs    SetExprs
	}

	// DDL represents a CREATE, ALTER, DROP or RENAME statement.
	// Table is set for AlterStr, DropStr, RenameStr.
	// NewName is set for AlterStr, CreateStr, RenameStr.
	DDL struct {
		Action          string
		Engine          string
		Charset         string
		IndexName       string
		PartitionName   string
		BackendName     string
		IfExists        bool
		IfNotExists     bool
		Table           TableName
		NewName         TableName
		Database        TableIdent
		DatabaseOptions DatabaseOptionListOpt
		TableSpec       *TableSpec

		// [UNIQUE | FULLTEXT | SPATIAL] index.
		IndexType string
		IndexOpts *IndexOptions

		// Tables is set if Action is DropStr.
		Tables TableNames

		// table column operation
		DropColumnName  string
		ModifyColumnDef *ColumnDefinition

		// Partition options.
		PartitionOptions PartitionOptions
		PartitionNum     *SQLVal
	}

	// Show represents a show statement.
	Show struct {
		Type     string
		Full     string
		Table    TableName
		Database TableName
		From     string
		Limit    *Limit
		Filter   *ShowFilter
	}

	// Checksum represents a CHECKSUM statement.
	Checksum struct {
		Table TableName
	}

	// Use represents a use statement.
	Use struct {
		DBName TableIdent
	}

	// OtherRead represents a DESCRIBE, or EXPLAIN statement.
	// It should be used only as an indicator. It does not contain
	// the full AST for the statement.
	OtherRead struct{}

	// OtherAdmin represents a misc statement that relies on ADMIN privileges,
	// such as REPAIR, OPTIMIZE, or TRUNCATE statement.
	// It should be used only as an indicator. It does not contain
	// the full AST for the statement.
	OtherAdmin struct{}

	// Radon represents the radon statement.
	Radon struct {
		Action  string
		Row     ValTuple
		Table   TableName
		NewName TableName
	}

	// Explain represents a explain statement.
	Explain struct{}

	// Kill represents a KILL statement.
	Kill struct {
		QueryID *NumVal
	}

	// Transaction represents the transaction tuple.
	Transaction struct {
		Action string
	}

	// Xa represents a XA statement.
	Xa struct{}
)

// ParenSelect can actually not be a top level statement,
// but we have to allow it because it's a requirement
// of SelectStatement.
func (*ParenSelect) iStatement() {}
func (*Union) iStatement()       {}
func (*Select) iStatement()      {}
func (*Insert) iStatement()      {}
func (*Update) iStatement()      {}
func (*Delete) iStatement()      {}
func (*Set) iStatement()         {}
func (*DDL) iStatement()         {}
func (*Show) iStatement()        {}
func (*Checksum) iStatement()    {}
func (*Use) iStatement()         {}
func (*OtherRead) iStatement()   {}
func (*OtherAdmin) iStatement()  {}
func (*Radon) iStatement()       {}
func (*Explain) iStatement()     {}
func (*Kill) iStatement()        {}
func (*Transaction) iStatement() {}
func (*Xa) iStatement()          {}

func (*Select) iSelectStatement()      {}
func (*Union) iSelectStatement()       {}
func (*ParenSelect) iSelectStatement() {}

type (
	// InsertRows represents the rows for an INSERT statement.
	InsertRows interface {
		iInsertRows()
		SQLNode
	}

	// Values represents a VALUES clause.
	Values []ValTuple
)

func (*Select) iInsertRows()      {}
func (*Union) iInsertRows()       {}
func (Values) iInsertRows()       {}
func (*ParenSelect) iInsertRows() {}

type DatabaseOptionListOpt struct {
	DBOptList []*DatabaseOption
}

// TableOptions is used by TableSpec
type TableOptions struct {
	Comment          string
	Engine           string
	Charset          string
	Type             string
	AvgRowLength     string
	Checksum         string
	Collate          string
	Compression      string
	Connection       string
	DataDirectory    string
	IndexDirectory   string
	DelayKeyWrite    string
	Encryption       string
	InsertMethod     string
	KeyBlockSize     string
	MaxRows          string
	MinRows          string
	PackKeys         string
	Password         string
	RowFormat        string
	StatsAutoRecalc  string
	StatsPersistent  string
	StatsSamplePages string
	TableSpace       string
}

// IndexOptions is used by IndexOpts.
type IndexOptions struct {
	Columns   []*IndexColumn
	Using     string
	Comment   string
	BlockSize *SQLVal
	Parser    string
	Algorithm string
	Lock      string
}

// IndexDefinition describes an index in a CREATE TABLE statement
type IndexDefinition struct {
	Type    string
	Name    ColIdent
	Opts    *IndexOptions
	Primary bool
	Unique  bool
}

// TableSpec describes the structure of a table from a CREATE TABLE statement
type TableSpec struct {
	Columns []*ColumnDefinition
	Indexes []*IndexDefinition
	Options TableOptions
}

// ColumnDefinition describes a column in a CREATE TABLE statement
type ColumnDefinition struct {
	Name ColIdent
	Type ColumnType
}

// ColumnType represents a sql type in a CREATE TABLE statement
// All optional fields are nil if not specified
type ColumnType struct {
	// The base type string
	Type string

	// Generic field options.
	NotNull       BoolVal
	Autoincrement BoolVal
	Default       *SQLVal
	OnUpdate      string
	Comment       *SQLVal
	Collate       *SQLVal
	ColumnFormat  string
	Storage       string

	// Numeric field options
	Length   *SQLVal
	Unsigned BoolVal
	Zerofill BoolVal
	Scale    *SQLVal

	// Text field options
	Charset string

	// Enum values
	EnumValues []string

	// Key specification
	PrimaryKeyOpt ColumnPrimaryKeyOption
	UniqueKeyOpt  ColumnUniqueKeyOption
}

// ShowFilter is show tables filter
type ShowFilter struct {
	Like   string
	Filter Expr
}

// Comments represents a list of comments.
type Comments [][]byte

// SelectExprs represents SELECT expressions.
type SelectExprs []SelectExpr

type (
	// SelectExpr represents a SELECT expression.
	SelectExpr interface {
		iSelectExpr()
		SQLNode
		// clone used to deep copy a new SelectExpr.
		clone() SelectExpr
	}

	// StarExpr defines a '*' or 'table.*' expression.
	StarExpr struct {
		TableName TableName
	}

	// AliasedExpr defines an aliased SELECT expression.
	AliasedExpr struct {
		Expr Expr
		As   ColIdent
	}

	// Nextval defines the NEXT VALUE expression.
	Nextval struct {
		Expr Expr
	}
)

func (*StarExpr) iSelectExpr()    {}
func (*AliasedExpr) iSelectExpr() {}
func (Nextval) iSelectExpr()      {}

// Columns represents an insert column list.
type Columns []ColIdent

// TableExprs represents a list of table expressions.
type TableExprs []TableExpr

type (
	// TableExpr represents a table expression.
	TableExpr interface {
		iTableExpr()
		SQLNode
	}

	// AliasedTableExpr represents a table expression
	// coupled with an optional alias or index hint.
	// If As is empty, no alias was used.
	AliasedTableExpr struct {
		Expr  SimpleTableExpr
		As    TableIdent
		Hints *IndexHints
	}

	// ParenTableExpr represents a parenthesized list of TableExpr.
	ParenTableExpr struct {
		Exprs TableExprs
	}

	// JoinTableExpr represents a TableExpr that's a JOIN operation.
	JoinTableExpr struct {
		LeftExpr  TableExpr
		Join      string
		RightExpr TableExpr
		On        Expr
	}
)

func (*AliasedTableExpr) iTableExpr() {}
func (*ParenTableExpr) iTableExpr()   {}
func (*JoinTableExpr) iTableExpr()    {}

type (
	// SimpleTableExpr represents a simple table expression.
	SimpleTableExpr interface {
		iSimpleTableExpr()
		SQLNode
	}

	// TableName represents a table  name.
	// Qualifier, if specified, represents a database or keyspace.
	// TableName is a value struct whose fields are case sensitive.
	// This means two TableName vars can be compared for equality
	// and a TableName can also be used as key in a map.
	TableName struct {
		Name, Qualifier TableIdent
	}

	// Subquery represents a subquery.
	Subquery struct {
		Select SelectStatement
	}
)

func (TableName) iSimpleTableExpr() {}
func (*Subquery) iSimpleTableExpr() {}

// TableNames is a list of TableName.
type TableNames []TableName

// IndexHints represents a list of index hints.
type IndexHints struct {
	Type    string
	Indexes []ColIdent
}

// Where represents a WHERE or HAVING clause.
type Where struct {
	Type string
	Expr Expr
}

// Exprs represents a list of value expressions.
// It's not a valid expression because it's not parenthesized.
type Exprs []Expr

// Expressions.
type (
	// Expr represents an expression.
	Expr interface {
		iExpr()
		SQLNode
		// clone used to deep copy a new Expr.
		clone() Expr
	}

	// AndExpr represents an AND expression.
	AndExpr struct {
		Left, Right Expr
	}

	// OrExpr represents an OR expression.
	OrExpr struct {
		Left, Right Expr
	}

	// NotExpr represents a NOT expression.
	NotExpr struct {
		Expr Expr
	}

	// ParenExpr represents a parenthesized boolean expression.
	ParenExpr struct {
		Expr Expr
	}

	// ComparisonExpr represents a two-value comparison expression.
	ComparisonExpr struct {
		Operator    string
		Left, Right Expr
		Escape      Expr
	}

	// RangeCond represents a BETWEEN or a NOT BETWEEN expression.
	RangeCond struct {
		Operator string
		Left     Expr
		From, To Expr
	}

	// IsExpr represents an IS ... or an IS NOT ... expression.
	IsExpr struct {
		Operator string
		Expr     Expr
	}

	// ExistsExpr represents an EXISTS expression.
	ExistsExpr struct {
		Subquery *Subquery
	}

	// SQLVal represents a single value.
	SQLVal struct {
		Type ValType
		Val  []byte
	}

	// NullVal represents a NULL value.
	NullVal struct{}

	// BoolVal is true or false.
	BoolVal bool

	// ColName represents a column name.
	ColName struct {
		// Metadata is not populated by the parser.
		// It's a placeholder for analyzers to store
		// additional data, typically info about which
		// table or column this node references.
		Metadata  interface{}
		Name      ColIdent
		Qualifier TableName
	}

	// ColTuple represents a list of column values.
	// It can be ValTuple, Subquery, ListArg.
	ColTuple interface {
		iColTuple()
		Expr
	}

	// ValTuple represents a tuple of actual values.
	ValTuple Exprs

	// ListArg represents a named list argument.
	ListArg []byte

	// BinaryExpr represents a binary value expression.
	BinaryExpr struct {
		Operator    string
		Left, Right Expr
	}

	// UnaryExpr represents a unary value expression.
	UnaryExpr struct {
		Operator string
		Expr     Expr
	}

	// IntervalExpr represents a date-time INTERVAL expression.
	IntervalExpr struct {
		Expr Expr
		Unit string
	}

	// CollateExpr represents dynamic collate operator.
	CollateExpr struct {
		Expr    Expr
		Charset string
	}

	// FuncExpr represents a function call.
	FuncExpr struct {
		Qualifier TableIdent
		Name      ColIdent
		Distinct  bool
		Exprs     SelectExprs
	}

	// GroupConcatExpr represents a call to GROUP_CONCAT
	GroupConcatExpr struct {
		Distinct  string
		Exprs     SelectExprs
		OrderBy   OrderBy
		Separator string
	}

	// ValuesFuncExpr represents a function call.
	ValuesFuncExpr struct {
		Name     ColIdent
		Resolved Expr
	}

	// ConvertExpr represents a call to CONVERT(expr, type)
	// or it's equivalent CAST(expr AS type). Both are rewritten to the former.
	ConvertExpr struct {
		Expr Expr
		Type *ConvertType
	}

	// ConvertUsingExpr represents a call to CONVERT(expr USING charset).
	ConvertUsingExpr struct {
		Expr Expr
		Type string
	}

	// MatchExpr represents a call to the MATCH function
	MatchExpr struct {
		Columns SelectExprs
		Expr    Expr
		Option  string
	}

	// CaseExpr represents a CASE expression.
	CaseExpr struct {
		Expr  Expr
		Whens []*When
		Else  Expr
	}

	// Default represents a DEFAULT expression.
	Default struct {
		ColName string
	}
)

func (*AndExpr) iExpr()          {}
func (*OrExpr) iExpr()           {}
func (*NotExpr) iExpr()          {}
func (*ParenExpr) iExpr()        {}
func (*ComparisonExpr) iExpr()   {}
func (*RangeCond) iExpr()        {}
func (*IsExpr) iExpr()           {}
func (*ExistsExpr) iExpr()       {}
func (*SQLVal) iExpr()           {}
func (*NullVal) iExpr()          {}
func (BoolVal) iExpr()           {}
func (*ColName) iExpr()          {}
func (ValTuple) iExpr()          {}
func (*Subquery) iExpr()         {}
func (ListArg) iExpr()           {}
func (*BinaryExpr) iExpr()       {}
func (*UnaryExpr) iExpr()        {}
func (*IntervalExpr) iExpr()     {}
func (*CollateExpr) iExpr()      {}
func (*FuncExpr) iExpr()         {}
func (*GroupConcatExpr) iExpr()  {}
func (*CaseExpr) iExpr()         {}
func (*ValuesFuncExpr) iExpr()   {}
func (*ConvertExpr) iExpr()      {}
func (*ConvertUsingExpr) iExpr() {}
func (*MatchExpr) iExpr()        {}
func (*Default) iExpr()          {}

func (ValTuple) iColTuple()  {}
func (*Subquery) iColTuple() {}
func (ListArg) iColTuple()   {}

// ConvertType represents the type in call to CONVERT(expr, type)
type ConvertType struct {
	Type     string
	Length   *SQLVal
	Scale    *SQLVal
	Operator string
	Charset  string
}

// When represents a WHEN sub-expression.
type When struct {
	Cond Expr
	Val  Expr
}

// GroupBy represents a GROUP BY clause.
type GroupBy Exprs

// OrderBy represents an ORDER By clause.
type OrderBy []*Order

// Order represents an ordering expression.
type Order struct {
	Expr      Expr
	Direction string
}

// Limit represents a LIMIT clause.
type Limit struct {
	Offset, Rowcount Expr
}

// UpdateExprs represents a list of update expressions.
type UpdateExprs []*UpdateExpr

// UpdateExpr represents an update expression.
type UpdateExpr struct {
	Name *ColName
	Expr Expr
}

// OnDup represents an ON DUPLICATE KEY clause.
type OnDup UpdateExprs

// SetExprs represents a list of set expressions.
type SetExprs []*SetExpr

// SetExpr represents a set expression.
type SetExpr struct {
	// global|session
	Scope string
	Type  ColIdent
	Val   SetVal
}

type (
	// SetVal represents a set variable value.
	SetVal interface {
		SQLNode
		iSetVal()
	}

	// OptVal represents the set variable value.
	// See https://dev.mysql.com/doc/refman/5.7/en/set-variable.html
	OptVal struct {
		Value Expr
	}

	// TxnVal represents the set-transaction characteristic.
	// See https://dev.mysql.com/doc/refman/5.7/en/set-transaction.html
	TxnVal struct {
		Level string
		Mode  string
	}
)

func (*OptVal) iSetVal() {}
func (*TxnVal) iSetVal() {}

// ColIdent is a case insensitive SQL identifier. It will be escaped with
// backquotes if necessary.
type ColIdent struct {
	// This artifact prevents this struct from being compared
	// with itself. It consumes no space as long as it's not the
	// last field in the struct.
	_            [0]struct{ _ []byte }
	val, lowered string
}

// TableIdent is a case sensitive SQL identifier. It will be escaped with
// backquotes if necessary.
type TableIdent struct {
	v string
}

// Format formats the node.
func (node *Select) Format(buf *TrackedBuffer) {
	buf.Myprintf("select %v%s%s%s%v from %v%v%v%v%v%v%s",
		node.Comments, node.Cache, node.Distinct, node.Hints, node.SelectExprs,
		node.From, node.Where,
		node.GroupBy, node.Having, node.OrderBy,
		node.Limit, node.Lock)
}

// AddOrder adds an order by element
func (node *Select) AddOrder(order *Order) {
	node.OrderBy = append(node.OrderBy, order)
}

// SetLimit sets the limit clause
func (node *Select) SetLimit(limit *Limit) {
	node.Limit = limit
}

// Format formats the node.
func (node *ParenSelect) Format(buf *TrackedBuffer) {
	buf.Myprintf("(%v)", node.Select)
}

// AddOrder adds an order by element
func (node *ParenSelect) AddOrder(order *Order) {
	panic("unreachable")
}

// SetLimit sets the limit clause
func (node *ParenSelect) SetLimit(limit *Limit) {
	panic("unreachable")
}

// Format formats the node.
func (node *Union) Format(buf *TrackedBuffer) {
	buf.Myprintf("%v %s %v%v%v%s", node.Left, node.Type, node.Right,
		node.OrderBy, node.Limit, node.Lock)
}

// AddOrder adds an order by element
func (node *Union) AddOrder(order *Order) {
	node.OrderBy = append(node.OrderBy, order)
}

// SetLimit sets the limit clause
func (node *Union) SetLimit(limit *Limit) {
	node.Limit = limit
}

// Format formats the node.
func (node *Insert) Format(buf *TrackedBuffer) {
	buf.Myprintf("%s %v%sinto %v%v %v%v",
		node.Action,
		node.Comments, node.Ignore,
		node.Table, node.Columns, node.Rows, node.OnDup)
}

// Format formats the node.
func (node *Update) Format(buf *TrackedBuffer) {
	buf.Myprintf("update %v%v set %v%v%v%v",
		node.Comments, node.Table,
		node.Exprs, node.Where, node.OrderBy, node.Limit)
}

// Format formats the node.
func (node *Delete) Format(buf *TrackedBuffer) {
	buf.Myprintf("delete %vfrom %v%v%v%v", node.Comments, node.Table, node.Where, node.OrderBy, node.Limit)
}

// Format formats the node.
func (node *Set) Format(buf *TrackedBuffer) {
	buf.Myprintf("set %v%v", node.Comments, node.Exprs)
}

// Format formats the node.
func (node *Checksum) Format(buf *TrackedBuffer) {
	buf.Myprintf("checksum table %v", node.Table)
}

// Format formats the node.
func (node *DDL) Format(buf *TrackedBuffer) {
	switch node.Action {
	case CreateDBStr:
		ifnotexists := ""
		if node.IfNotExists {
			ifnotexists = " if not exists"
		}
		buf.Myprintf("%s%s %s", node.Action, ifnotexists, node.Database.String())
		node.DatabaseOptions.Format(buf)
	case DropDBStr:
		exists := ""
		if node.IfExists {
			exists = " if exists"
		}
		buf.Myprintf("%s%s %s", node.Action, exists, node.Database.String())
	case CreateTableStr:
		ifnotexists := ""
		if node.IfNotExists {
			ifnotexists = " if not exists"
		}
		if node.TableSpec == nil {
			buf.Myprintf("%s%s %v", node.Action, ifnotexists, node.NewName)
		} else {
			buf.Myprintf("%s%s %v %v", node.Action, ifnotexists, node.NewName, node.TableSpec)
		}
	case CreateIndexStr:
		buf.Myprintf("create %s%s on %v%v", node.IndexType, node.IndexName, node.NewName, node.IndexOpts)
	case DropTableStr:
		exists := ""
		if node.IfExists {
			exists = " if exists"
		}
		buf.Myprintf("%s%s %v", node.Action, exists, node.Tables)
	case DropIndexStr:
		buf.Myprintf("%s %s on %v", node.Action, node.IndexName, node.Table)
	case RenameStr:
		buf.Myprintf("%s %v to %v", node.Action, node.Table, node.NewName)
	case AlterStr:
		buf.Myprintf("%s table %v", node.Action, node.NewName)
	case AlterEngineStr:
		buf.Myprintf("%s %v engine = %s", node.Action, node.NewName, node.Engine)
	case AlterCharsetStr:
		buf.Myprintf("alter table %v convert to character set %s", node.NewName, node.Charset)
	case AlterAddColumnStr:
		buf.Myprintf("alter table %v add column %v", node.NewName, node.TableSpec)
	case AlterDropColumnStr:
		buf.Myprintf("alter table %v drop column `%s`", node.NewName, node.DropColumnName)
	case AlterModifyColumnStr:
		buf.Myprintf("alter table %v modify column %v", node.NewName, node.ModifyColumnDef)
	case TruncateTableStr:
		buf.Myprintf("%s %v", node.Action, node.NewName)
	}
}

// Format formats the node
func (optList DatabaseOptionListOpt) Format(buf *TrackedBuffer) {
	for _, dbOpt := range optList.DBOptList {
		buf.Myprintf(" %s %s", dbOpt.CharsetOrCollate, dbOpt.Value)
	}
}

// Format formats the node.
func (opts TableOptions) Format(buf *TrackedBuffer) {
	if opts.Comment != "" {
		buf.Myprintf(" comment=%s", opts.Comment)
	}
	if opts.Engine != "" {
		buf.Myprintf(" engine=%s", opts.Engine)
	}
	if opts.Charset != "" {
		buf.Myprintf(" default charset=%s", opts.Charset)
	}
	if opts.AvgRowLength != "" {
		buf.Myprintf(" avg_row_length=%s", opts.AvgRowLength)
	}
	if opts.Checksum != "" {
		buf.Myprintf(" checksum=%s", opts.Checksum)
	}
	if opts.Collate != "" {
		buf.Myprintf(" collate=%s", opts.Collate)
	}
	if opts.Compression != "" {
		buf.Myprintf(" compression=%s", opts.Compression)
	}
	if opts.Connection != "" {
		buf.Myprintf(" connection=%s", opts.Connection)
	}
	if opts.DataDirectory != "" {
		buf.Myprintf(" data directory=%s", opts.DataDirectory)
	}
	if opts.IndexDirectory != "" {
		buf.Myprintf(" index directory=%s", opts.IndexDirectory)
	}
	if opts.DelayKeyWrite != "" {
		buf.Myprintf(" delay_key_write=%s", opts.DelayKeyWrite)
	}
	if opts.Encryption != "" {
		buf.Myprintf(" encryption=%s", opts.Encryption)
	}
	if opts.InsertMethod != "" {
		buf.Myprintf(" insert_method=%s", opts.InsertMethod)
	}
	if opts.InsertMethod != "" {
		buf.Myprintf(" key_block_size=%s", opts.KeyBlockSize)
	}
	if opts.MaxRows != "" {
		buf.Myprintf(" max_rows=%s", opts.MaxRows)
	}
	if opts.MinRows != "" {
		buf.Myprintf(" min_rows=%s", opts.MinRows)
	}
	if opts.PackKeys != "" {
		buf.Myprintf(" pack_keys=%s", opts.PackKeys)
	}
	if opts.Password != "" {
		buf.Myprintf(" password=%s", opts.Password)
	}
	if opts.RowFormat != "" {
		buf.Myprintf(" row_format=%s", opts.RowFormat)
	}
	if opts.StatsAutoRecalc != "" {
		buf.Myprintf(" stats_auto_recalc=%s", opts.StatsAutoRecalc)
	}
	if opts.StatsPersistent != "" {
		buf.Myprintf(" stats_persistent=%s", opts.StatsPersistent)
	}
	if opts.StatsSamplePages != "" {
		buf.Myprintf(" stats_sample_pages=%s", opts.StatsSamplePages)
	}
	if opts.TableSpace != "" {
		buf.Myprintf(" tablespace=%s", opts.TableSpace)
	}
}

// Format formats the node.
func (opts *IndexOptions) Format(buf *TrackedBuffer) {
	buf.Myprintf("(")
	for i, col := range opts.Columns {
		if i != 0 {
			buf.Myprintf(", `%s`", col.Column.String())
		} else {
			buf.Myprintf("`%s`", col.Column.String())
		}
		if col.Length != nil {
			buf.Myprintf("(%v)", col.Length)
		}
	}
	buf.Myprintf(")")
	if opts.Using != "" {
		buf.Myprintf(" using %s", opts.Using)
	}
	if opts.Comment != "" {
		buf.Myprintf(" comment %s", opts.Comment)
	}
	if opts.BlockSize != nil {
		buf.Myprintf(" key_block_size = %v", opts.BlockSize)
	}
	if opts.Parser != "" {
		buf.Myprintf(" WITH PARSER %s", opts.Parser)
	}
	if opts.Algorithm != "" {
		buf.Myprintf(" algorithm = %s", opts.Algorithm)
	}
	if opts.Lock != "" {
		buf.Myprintf(" lock = %s", opts.Lock)
	}
}

// Format formats the node.
func (ts *TableSpec) Format(buf *TrackedBuffer) {
	buf.Myprintf("(\n")
	for i, col := range ts.Columns {
		if i == 0 {
			buf.Myprintf("\t%v", col)
		} else {
			buf.Myprintf(",\n\t%v", col)
		}
	}
	for _, idx := range ts.Indexes {
		buf.Myprintf(",\n\t%v", idx)
	}
	buf.Myprintf("\n)%v", ts.Options)
}

// Format formats the node.
func (col *ColumnDefinition) Format(buf *TrackedBuffer) {
	buf.Myprintf("`%s` %v", col.Name.String(), &col.Type)
}

// Format returns a canonical string representation of the type and all relevant options
func (ct *ColumnType) Format(buf *TrackedBuffer) {
	buf.Myprintf("%s", ct.Type)

	if ct.Length != nil && ct.Scale != nil {
		buf.Myprintf("(%v,%v)", ct.Length, ct.Scale)

	} else if ct.Length != nil {
		buf.Myprintf("(%v)", ct.Length)
	}

	if ct.EnumValues != nil {
		buf.Myprintf("(%s)", strings.Join(ct.EnumValues, ", "))
	}

	opts := make([]string, 0, 16)
	if ct.Unsigned {
		opts = append(opts, keywordStrings[UNSIGNED])
	}
	if ct.Zerofill {
		opts = append(opts, keywordStrings[ZEROFILL])
	}
	if ct.Charset != "" {
		opts = append(opts, keywordStrings[CHARACTER], keywordStrings[SET], ct.Charset)
	}
	if ct.Collate != nil {
		opts = append(opts, keywordStrings[COLLATE], String(ct.Collate))
	}
	if ct.ColumnFormat != "" {
		opts = append(opts, keywordStrings[COLUMN_FORMAT], ct.ColumnFormat)
	}
	if ct.Storage != "" {
		opts = append(opts, keywordStrings[STORAGE], ct.Storage)
	}
	if ct.NotNull {
		opts = append(opts, keywordStrings[NOT], keywordStrings[NULL])
	}
	if ct.Default != nil {
		opts = append(opts, keywordStrings[DEFAULT], String(ct.Default))
	}
	if ct.OnUpdate != "" {
		opts = append(opts, keywordStrings[ON], keywordStrings[UPDATE], ct.OnUpdate)
	}
	if ct.Autoincrement {
		opts = append(opts, keywordStrings[AUTO_INCREMENT])
	}
	if ct.Comment != nil {
		opts = append(opts, keywordStrings[COMMENT_KEYWORD], String(ct.Comment))
	}
	if ct.PrimaryKeyOpt == ColKeyPrimary {
		opts = append(opts, keywordStrings[PRIMARY], keywordStrings[KEY])
	}
	if ct.UniqueKeyOpt == ColKeyUniqueKey {
		opts = append(opts, keywordStrings[UNIQUE], keywordStrings[KEY])
	}

	if len(opts) != 0 {
		buf.Myprintf(" %s", strings.Join(opts, " "))
	}
}

// Format formats the node.
func (idx *IndexDefinition) Format(buf *TrackedBuffer) {
	if idx.Primary {
		buf.Myprintf("%s", idx.Type)
	} else {
		buf.Myprintf("%s `%v`", idx.Type, idx.Name)
	}
	buf.Myprintf(" %v", idx.Opts)
}

// Format formats the node.
func (node *Show) Format(buf *TrackedBuffer) {
	switch node.Type {
	case ShowCreateDatabaseStr:
		buf.Myprintf("show %s %v", node.Type, node.Database)
	case ShowTableStatusStr:
		buf.Myprintf("show %s", node.Type)
		if node.Database.Name.String() != "" {
			buf.Myprintf(" from %s", node.Database.Name.String())
		}
	case ShowCreateTableStr:
		buf.Myprintf("show %s %v", node.Type, node.Table)
	case ShowTablesStr:
		buf.Myprintf("show %s%s", node.Full, node.Type)
		if node.Database.Name.String() != "" {
			buf.Myprintf(" from %s", node.Database.Name.String())
		}
		if node.Filter != nil {
			buf.Myprintf("%v", node.Filter)
		}
	case ShowBinlogEventsStr:
		buf.Myprintf("show %s", node.Type)
		if node.From != "" {
			buf.Myprintf(" from gtid '%s'", node.From)
		}
		buf.Myprintf("%v", node.Limit)
	case ShowColumnsStr:
		buf.Myprintf("show %s%s", node.Full, node.Type)
		if node.Table.Name.String() != "" {
			buf.Myprintf(" from %v", node.Table)
		}
		if node.Filter != nil {
			buf.Myprintf("%v", node.Filter)
		}
	default:
		buf.Myprintf("show %s", node.Type)
	}
}

// Format formats the node.
func (node *ShowFilter) Format(buf *TrackedBuffer) {
	if node == nil {
		return
	}
	if node.Like != "" {
		buf.Myprintf(" like '%s'", node.Like)
	} else {
		buf.Myprintf(" where %v", node.Filter)
	}
}

// Format formats the node.
func (node *Use) Format(buf *TrackedBuffer) {
	buf.Myprintf("use %v", node.DBName)
}

// Format formats the node.
func (node *OtherRead) Format(buf *TrackedBuffer) {
	buf.WriteString("otherread")
}

// Format formats the node.
func (node *OtherAdmin) Format(buf *TrackedBuffer) {
	buf.WriteString("otheradmin")
}

// Format formats the node.
func (node Comments) Format(buf *TrackedBuffer) {
	for _, c := range node {
		buf.Myprintf("%s ", c)
	}
}

// Format formats the node.
func (node SelectExprs) Format(buf *TrackedBuffer) {
	var prefix string
	for _, n := range node {
		buf.Myprintf("%s%v", prefix, n)
		prefix = ", "
	}
}

// Format formats the node.
func (node *StarExpr) Format(buf *TrackedBuffer) {
	if !node.TableName.IsEmpty() {
		buf.Myprintf("%v.", node.TableName)
	}
	buf.Myprintf("*")
}

func (node *StarExpr) clone() SelectExpr {
	return &StarExpr{
		TableName: node.TableName,
	}
}

// Format formats the node.
func (node *AliasedExpr) Format(buf *TrackedBuffer) {
	buf.Myprintf("%v", node.Expr)
	if !node.As.IsEmpty() {
		buf.Myprintf(" as %v", node.As)
	}
}

func (node *AliasedExpr) clone() SelectExpr {
	return &AliasedExpr{
		Expr: CloneExpr(node.Expr),
		As:   node.As,
	}
}

// Format formats the node.
func (node Nextval) Format(buf *TrackedBuffer) {
	buf.Myprintf("next %v values", node.Expr)
}

func (node Nextval) clone() SelectExpr {
	return Nextval{
		Expr: CloneExpr(node.Expr),
	}
}

// Format formats the node.
func (node Columns) Format(buf *TrackedBuffer) {
	if node == nil {
		return
	}
	prefix := "("
	for _, n := range node {
		buf.Myprintf("%s%v", prefix, n)
		prefix = ", "
	}
	buf.WriteString(")")
}

// Format formats the node.
func (node TableExprs) Format(buf *TrackedBuffer) {
	var prefix string
	for _, n := range node {
		buf.Myprintf("%s%v", prefix, n)
		prefix = ", "
	}
}

// Format formats the node.
func (node *AliasedTableExpr) Format(buf *TrackedBuffer) {
	buf.Myprintf("%v", node.Expr)
	if !node.As.IsEmpty() {
		buf.Myprintf(" as %v", node.As)
	}
	if node.Hints != nil {
		// Hint node provides the space padding.
		buf.Myprintf("%v", node.Hints)
	}
}

// Format formats the node.
func (node TableNames) Format(buf *TrackedBuffer) {
	var prefix string
	for _, n := range node {
		buf.Myprintf("%s%v", prefix, n)
		prefix = ", "
	}
}

// Format formats the node.
func (node TableName) Format(buf *TrackedBuffer) {
	if node.IsEmpty() {
		return
	}
	if !node.Qualifier.IsEmpty() {
		buf.Myprintf("%v.", node.Qualifier)
	}
	buf.Myprintf("%v", node.Name)
}

// Format formats the node.
func (node *ParenTableExpr) Format(buf *TrackedBuffer) {
	buf.Myprintf("(%v)", node.Exprs)
}

// Format formats the node.
func (node *JoinTableExpr) Format(buf *TrackedBuffer) {
	buf.Myprintf("%v %s %v", node.LeftExpr, node.Join, node.RightExpr)
	if node.On != nil {
		buf.Myprintf(" on %v", node.On)
	}
}

// Format formats the node.
func (node *IndexHints) Format(buf *TrackedBuffer) {
	buf.Myprintf(" %sindex ", node.Type)
	prefix := "("
	for _, n := range node.Indexes {
		buf.Myprintf("%s%v", prefix, n)
		prefix = ", "
	}
	buf.Myprintf(")")
}

// Format formats the node.
func (node *Where) Format(buf *TrackedBuffer) {
	if node == nil || node.Expr == nil {
		return
	}
	buf.Myprintf(" %s %v", node.Type, node.Expr)
}

// Format formats the node.
func (node Exprs) Format(buf *TrackedBuffer) {
	var prefix string
	for _, n := range node {
		buf.Myprintf("%s%v", prefix, n)
		prefix = ", "
	}
}

// Format formats the node.
func (node *AndExpr) Format(buf *TrackedBuffer) {
	buf.Myprintf("%v and %v", node.Left, node.Right)
}

func (node *AndExpr) clone() Expr {
	return &AndExpr{
		Left:  CloneExpr(node.Left),
		Right: CloneExpr(node.Right),
	}
}

// Format formats the node.
func (node *OrExpr) Format(buf *TrackedBuffer) {
	buf.Myprintf("%v or %v", node.Left, node.Right)
}

func (node *OrExpr) clone() Expr {
	return &OrExpr{
		Left:  CloneExpr(node.Left),
		Right: CloneExpr(node.Right),
	}
}

// Format formats the node.
func (node *NotExpr) Format(buf *TrackedBuffer) {
	buf.Myprintf("not %v", node.Expr)
}

func (node *NotExpr) clone() Expr {
	return &NotExpr{
		Expr: CloneExpr(node.Expr),
	}
}

// Format formats the node.
func (node *ParenExpr) Format(buf *TrackedBuffer) {
	buf.Myprintf("(%v)", node.Expr)
}

func (node *ParenExpr) clone() Expr {
	return &ParenExpr{
		Expr: CloneExpr(node.Expr),
	}
}

// Format formats the node.
func (node *ComparisonExpr) Format(buf *TrackedBuffer) {
	buf.Myprintf("%v %s %v", node.Left, node.Operator, node.Right)
	if node.Escape != nil {
		buf.Myprintf(" escape %v", node.Escape)
	}
}

func (node *ComparisonExpr) clone() Expr {
	return &ComparisonExpr{
		Operator: node.Operator,
		Left:     CloneExpr(node.Left),
		Right:    CloneExpr(node.Right),
		Escape:   CloneExpr(node.Escape),
	}
}

// Format formats the node.
func (node *RangeCond) Format(buf *TrackedBuffer) {
	buf.Myprintf("%v %s %v and %v", node.Left, node.Operator, node.From, node.To)
}

func (node *RangeCond) clone() Expr {
	return &RangeCond{
		Operator: node.Operator,
		Left:     CloneExpr(node.Left),
		From:     CloneExpr(node.From),
		To:       CloneExpr(node.To),
	}
}

// Format formats the node.
func (node *IsExpr) Format(buf *TrackedBuffer) {
	buf.Myprintf("%v %s", node.Expr, node.Operator)
}

func (node *IsExpr) clone() Expr {
	return &IsExpr{
		Operator: node.Operator,
		Expr:     CloneExpr(node.Expr),
	}
}

// Format formats the node.
func (node *ExistsExpr) Format(buf *TrackedBuffer) {
	buf.Myprintf("exists %v", node.Subquery)
}

func (node *ExistsExpr) clone() Expr {
	return nil
}

// Format formats the node.
func (node *SQLVal) Format(buf *TrackedBuffer) {
	switch node.Type {
	case StrVal:
		sqltypes.MakeTrusted(sqltypes.VarBinary, node.Val).EncodeSQL(buf)
	case IntVal, FloatVal, HexNum, StrValWithoutQuote:
		buf.Myprintf("%s", []byte(node.Val))
	case HexVal:
		buf.Myprintf("X'%s'", []byte(node.Val))
	case ValArg:
		buf.WriteArg(string(node.Val))
	default:
		panic("unexpected")
	}
}

func (node *SQLVal) clone() Expr {
	return &SQLVal{
		Type: node.Type,
		Val:  node.Val,
	}
}

// Format formats the node.
func (node *NullVal) Format(buf *TrackedBuffer) {
	buf.Myprintf("null")
}

func (node *NullVal) clone() Expr {
	return &NullVal{}
}

// Format formats the node.
func (node BoolVal) Format(buf *TrackedBuffer) {
	if node {
		buf.Myprintf("true")
	} else {
		buf.Myprintf("false")
	}
}

func (node BoolVal) clone() Expr {
	return node
}

// Format formats the node.
func (node *ColName) Format(buf *TrackedBuffer) {
	if !node.Qualifier.IsEmpty() {
		buf.Myprintf("%v.", node.Qualifier)
	}
	buf.Myprintf("%v", node.Name)
}

func (node *ColName) clone() Expr {
	return &ColName{
		Name:      node.Name,
		Qualifier: node.Qualifier,
	}
}

// Format formats the node.
func (node ValTuple) Format(buf *TrackedBuffer) {
	buf.Myprintf("(%v)", Exprs(node))
}

func (node ValTuple) clone() Expr {
	var tuple ValTuple
	for _, e := range node {
		exp := CloneExpr(e)
		tuple = append(tuple, exp)
	}
	return tuple
}

// Format formats the node.
func (node *Subquery) Format(buf *TrackedBuffer) {
	buf.Myprintf("(%v)", node.Select)
}

func (node *Subquery) clone() Expr {
	return nil
}

// Format formats the node.
func (node ListArg) Format(buf *TrackedBuffer) {
	buf.WriteArg(string(node))
}

func (node ListArg) clone() Expr {
	return node
}

// Format formats the node.
func (node *BinaryExpr) Format(buf *TrackedBuffer) {
	buf.Myprintf("%v %s %v", node.Left, node.Operator, node.Right)
}

func (node *BinaryExpr) clone() Expr {
	return &BinaryExpr{
		Operator: node.Operator,
		Left:     CloneExpr(node.Left),
		Right:    CloneExpr(node.Right),
	}
}

// Format formats the node.
func (node *UnaryExpr) Format(buf *TrackedBuffer) {
	if _, unary := node.Expr.(*UnaryExpr); unary {
		buf.Myprintf("%s %v", node.Operator, node.Expr)
		return
	}
	buf.Myprintf("%s%v", node.Operator, node.Expr)
}

func (node *UnaryExpr) clone() Expr {
	return &UnaryExpr{
		Operator: node.Operator,
		Expr:     CloneExpr(node.Expr),
	}
}

// Format formats the node.
func (node *IntervalExpr) Format(buf *TrackedBuffer) {
	buf.Myprintf("interval %v %s", node.Expr, node.Unit)
}

func (node *IntervalExpr) clone() Expr {
	return &IntervalExpr{
		Expr: CloneExpr(node.Expr),
		Unit: node.Unit,
	}
}

// Format formats the node.
func (node *CollateExpr) Format(buf *TrackedBuffer) {
	buf.Myprintf("%v", node.Expr)
	if node.Charset != "" {
		buf.Myprintf(" collate %s", node.Charset)
	}
}

func (node *CollateExpr) clone() Expr {
	return &CollateExpr{
		Expr:    CloneExpr(node.Expr),
		Charset: node.Charset,
	}
}

// Format formats the node.
func (node *FuncExpr) Format(buf *TrackedBuffer) {
	var distinct string
	if node.Distinct {
		distinct = "distinct "
	}
	if !node.Qualifier.IsEmpty() {
		buf.Myprintf("%v.", node.Qualifier)
	}
	// Function names should not be back-quoted even
	// if they match a reserved word. So, print the
	// name as is.
	buf.Myprintf("%s(%s%v)", node.Name.String(), distinct, node.Exprs)
}

func (node *FuncExpr) clone() Expr {
	var exprs SelectExprs
	for _, e := range node.Exprs {
		exp := CloneSelectExpr(e)
		exprs = append(exprs, exp)
	}

	return &FuncExpr{
		Qualifier: node.Qualifier,
		Name:      node.Name,
		Distinct:  node.Distinct,
		Exprs:     exprs,
	}
}

// Format formats the node
func (node *GroupConcatExpr) Format(buf *TrackedBuffer) {
	buf.Myprintf("group_concat(%s%v%v%s)", node.Distinct, node.Exprs, node.OrderBy, node.Separator)
}

func (node *GroupConcatExpr) clone() Expr {
	var exprs SelectExprs
	for _, e := range node.Exprs {
		exp := CloneSelectExpr(e)
		exprs = append(exprs, exp)
	}

	var orders OrderBy
	for _, order := range node.OrderBy {
		o := &Order{
			Expr:      CloneExpr(order.Expr),
			Direction: order.Direction,
		}
		orders = append(orders, o)
	}

	return &GroupConcatExpr{
		Distinct:  node.Distinct,
		Exprs:     exprs,
		OrderBy:   orders,
		Separator: node.Separator,
	}
}

// Format formats the node.
func (node *ValuesFuncExpr) Format(buf *TrackedBuffer) {
	// Function names should not be back-quoted even
	// if they match a reserved word. So, print the
	// name as is.
	if node.Resolved != nil {
		buf.Myprintf("%v", node.Resolved)
	} else {
		buf.Myprintf("values(%s)", node.Name.String())
	}
}

func (node *ValuesFuncExpr) clone() Expr {
	return &ValuesFuncExpr{
		Name:     node.Name,
		Resolved: CloneExpr(node.Resolved),
	}
}

// Format formats the node.
func (node *ConvertExpr) Format(buf *TrackedBuffer) {
	buf.Myprintf("convert(%v, %v)", node.Expr, node.Type)
}

func (node *ConvertExpr) clone() Expr {
	return &ConvertExpr{
		Expr: CloneExpr(node.Expr),
		Type: node.Type,
	}
}

// Format formats the node.
func (node *ConvertUsingExpr) Format(buf *TrackedBuffer) {
	buf.Myprintf("convert(%v using %s)", node.Expr, node.Type)
}

func (node *ConvertUsingExpr) clone() Expr {
	return &ConvertUsingExpr{
		Expr: CloneExpr(node.Expr),
		Type: node.Type,
	}
}

// Format formats the node.
func (node *ConvertType) Format(buf *TrackedBuffer) {
	buf.Myprintf("%s", node.Type)
	if node.Length != nil {
		buf.Myprintf("(%v", node.Length)
		if node.Scale != nil {
			buf.Myprintf(", %v", node.Scale)
		}
		buf.Myprintf(")")
	}
	if node.Charset != "" {
		buf.Myprintf("%s %s", node.Operator, node.Charset)
	}
}

// Format formats the node
func (node *MatchExpr) Format(buf *TrackedBuffer) {
	buf.Myprintf("match(%v) against (%v%s)", node.Columns, node.Expr, node.Option)
}

func (node *MatchExpr) clone() Expr {
	var exprs SelectExprs
	for _, e := range node.Columns {
		exp := CloneSelectExpr(e)
		exprs = append(exprs, exp)
	}

	return &MatchExpr{
		Columns: exprs,
		Expr:    CloneExpr(node.Expr),
		Option:  node.Option,
	}
}

// Format formats the node.
func (node *CaseExpr) Format(buf *TrackedBuffer) {
	buf.Myprintf("case ")
	if node.Expr != nil {
		buf.Myprintf("%v ", node.Expr)
	}
	for _, when := range node.Whens {
		buf.Myprintf("%v ", when)
	}
	if node.Else != nil {
		buf.Myprintf("else %v ", node.Else)
	}
	buf.Myprintf("end")
}

func (node *CaseExpr) clone() Expr {
	var whens []*When
	for _, when := range node.Whens {
		w := &When{
			Cond: CloneExpr(when.Cond),
			Val:  CloneExpr(when.Val),
		}
		whens = append(whens, w)
	}

	return &CaseExpr{
		Expr:  CloneExpr(node.Expr),
		Whens: whens,
		Else:  CloneExpr(node.Else),
	}
}

// Format formats the node.
func (node *Default) Format(buf *TrackedBuffer) {
	buf.Myprintf("default")
	if node.ColName != "" {
		buf.Myprintf("(%s)", node.ColName)
	}
}

func (node *Default) clone() Expr {
	return &Default{
		ColName: node.ColName,
	}
}

// Format formats the node.
func (node *When) Format(buf *TrackedBuffer) {
	buf.Myprintf("when %v then %v", node.Cond, node.Val)
}

// Format formats the node.
func (node GroupBy) Format(buf *TrackedBuffer) {
	prefix := " group by "
	for _, n := range node {
		buf.Myprintf("%s%v", prefix, n)
		prefix = ", "
	}
}

// Format formats the node.
func (node OrderBy) Format(buf *TrackedBuffer) {
	prefix := " order by "
	for _, n := range node {
		buf.Myprintf("%s%v", prefix, n)
		prefix = ", "
	}
}

// Format formats the node.
func (node *Order) Format(buf *TrackedBuffer) {
	if node, ok := node.Expr.(*NullVal); ok {
		buf.Myprintf("%v", node)
		return
	}
	buf.Myprintf("%v %s", node.Expr, node.Direction)
}

// Format formats the node.
func (node *Limit) Format(buf *TrackedBuffer) {
	if node == nil {
		return
	}
	buf.Myprintf(" limit ")
	if node.Offset != nil {
		buf.Myprintf("%v, ", node.Offset)
	}
	buf.Myprintf("%v", node.Rowcount)
}

// Format formats the node.
func (node Values) Format(buf *TrackedBuffer) {
	prefix := "values "
	for _, n := range node {
		buf.Myprintf("%s%v", prefix, n)
		prefix = ", "
	}
}

// Format formats the node.
func (node UpdateExprs) Format(buf *TrackedBuffer) {
	var prefix string
	for _, n := range node {
		buf.Myprintf("%s%v", prefix, n)
		prefix = ", "
	}
}

// Format formats the node.
func (node *UpdateExpr) Format(buf *TrackedBuffer) {
	buf.Myprintf("%v = %v", node.Name, node.Expr)
}

// Format formats the node.
func (node OnDup) Format(buf *TrackedBuffer) {
	if node == nil {
		return
	}
	buf.Myprintf(" on duplicate key update %v", UpdateExprs(node))
}

// Format formats the node.
func (node SetExprs) Format(buf *TrackedBuffer) {
	var prefix string
	for _, n := range node {
		buf.Myprintf("%s%v", prefix, n)
		prefix = ", "
	}
}

// Format formats the node.
func (node *SetExpr) Format(buf *TrackedBuffer) {
	if node.Scope != "" {
		buf.WriteString(node.Scope)
		buf.WriteString(" ")
	}
	// We don't have to backtick set variable names.
	switch {
	case node.Type.EqualString("charset") || node.Type.EqualString("names") || node.Type.EqualString("transaction"):
		buf.Myprintf("%s %v", node.Type.String(), node.Val)
	default:
		buf.Myprintf("%s = %v", node.Type.String(), node.Val)
	}
}

// Format formats the node.
func (node *OptVal) Format(buf *TrackedBuffer) {
	buf.Myprintf("%v", node.Value)
}

// Format formats the node.
func (node *TxnVal) Format(buf *TrackedBuffer) {
	var prefix string
	if node.Level != "" {
		buf.WriteString("isolation level " + node.Level)
		prefix = ", "
	}
	if node.Mode != "" {
		buf.Myprintf("%s%s", prefix, node.Mode)
	}
}

// Format formats the node.
func (node ColIdent) Format(buf *TrackedBuffer) {
	formatID(buf, node.val, node.Lowered())
}

// Format formats the node.
func (node TableIdent) Format(buf *TrackedBuffer) {
	formatID(buf, node.v, strings.ToLower(node.v))
}

// Format formats the node.
func (node *Radon) Format(buf *TrackedBuffer) {
	switch node.Action {
	case AttachListStr:
		buf.Myprintf("radon %s", node.Action)
	case AttachStr, DetachStr:
		buf.Myprintf("radon %s %v", node.Action, node.Row)
	case ReshardStr:
		buf.Myprintf("radon %s %v to %v", node.Action, node.Table, node.NewName)
	case CleanupStr:
		buf.Myprintf("radon %s", node.Action)
	}
}

// Format formats the node.
func (node *Explain) Format(buf *TrackedBuffer) {
	buf.WriteString("explain")
}

// Format formats the node.
func (node *Kill) Format(buf *TrackedBuffer) {
	buf.Myprintf("kill %s", node.QueryID.raw)
}

// Format formats the node.
func (node *Transaction) Format(buf *TrackedBuffer) {
	switch node.Action {
	case StartTxnStr:
		buf.WriteString(StartTxnStr)
	case BeginTxnStr:
		buf.WriteString(BeginTxnStr)
	case RollbackTxnStr:
		buf.WriteString(RollbackTxnStr)
	case CommitTxnStr:
		buf.WriteString(CommitTxnStr)
	}
}

// Format formats the node.
func (node *Xa) Format(buf *TrackedBuffer) {
	buf.WriteString("XA")
}
